//
//  OttoMateAction.m
//  Set Form Values of Current Webpage
//
//  Created by itod on 3/30/07.
//  Copyright 2007 Todd Ditchendorf. All rights reserved.
//

#import "OttoMateAction.h"
#import "TODComparisonValueTransformer.h"
#import <OSAKit/OSAKit.h>

static NSString *errorHandlerScriptPath = nil;
static NSString *assertionFailedScriptPath = nil;
static NSString *delay = @"0";

@interface OttoMateAction (Private)
+ (void)registerValueTransformers;
- (void)runScriptAtPath:(NSString *)path withArg:(NSString *)arg;
@end

@implementation OttoMateAction

+ (void)initialize
{
	[self registerValueTransformers];
}


+ (void)registerValueTransformers
{
	TODComparisonValueTransformer *gtOneValueTransformer = nil;
	gtOneValueTransformer = [[TODComparisonValueTransformer alloc] initWithNumber:1
																	comparisonType:TODComparisonTypeGreaterThan];
	[NSValueTransformer setValueTransformer:gtOneValueTransformer 
									forName:@"TODIsGreaterThanOne"];
	[gtOneValueTransformer release];

	TODComparisonValueTransformer *ltSevenValueTransformer = nil;
	ltSevenValueTransformer = [[TODComparisonValueTransformer alloc] initWithNumber:7
																	 comparisonType:TODComparisonTypeLessThan];
	[NSValueTransformer setValueTransformer:ltSevenValueTransformer
									forName:@"TODIsLessThanSeven"];
	[ltSevenValueTransformer release];

	TODComparisonValueTransformer *ltEightValueTransformer = nil;
	ltEightValueTransformer = [[TODComparisonValueTransformer alloc] initWithNumber:8
																	 comparisonType:TODComparisonTypeLessThan];
	[NSValueTransformer setValueTransformer:ltEightValueTransformer 
									forName:@"TODIsLessThanEight"];
	[ltEightValueTransformer release];
}


+ (void)setErrorHandlerScriptPath:(NSString *)path
{
	if (errorHandlerScriptPath != path) {
		[errorHandlerScriptPath autorelease];
		errorHandlerScriptPath = [path retain];
	}
}


+ (NSString *)errorHandlerScriptPath
{
	return errorHandlerScriptPath;	
}


+ (void)setAssertionFailedScriptPath:(NSString *)path
{
	if (assertionFailedScriptPath != path) {
		[assertionFailedScriptPath autorelease];
		assertionFailedScriptPath = [path retain];
	}
}


+ (NSString *)assertionFailedScriptPath
{
	return assertionFailedScriptPath;
}


+ (void)setDelay:(NSString *)newStr
{
	if (delay != newStr) {
		[delay autorelease];
		delay = [newStr retain];
	}
}


+ (NSString *)delay
{
	return delay;
}


#pragma mark -


- (id)initWithDefinition:(NSDictionary *)dict fromArchive:(BOOL)archived;
{
	self = [super initWithDefinition:dict fromArchive:archived];
	if (self != nil) {
		[self setInputParameters:[NSMutableDictionary dictionary]];
	}
	return self;
}


- (void)dealloc
{
	[self setInput:nil];
	[self setInputParameters:nil];
	[super dealloc];
}


#pragma mark -
#pragma mark AMBundleAction

- (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo
{
	//NSLog(@"%s params %@", _cmd, [self parameters]);
	[self setInput:input];
	
	[self checkParametersForInputVariable];
	
	[self executeAndReturnError:errorInfo];
	
	return input;
}


#pragma mark -
#pragma mark Protected Methods

- (NSString *)appleScriptFilePath
{
	NSBundle *bundle = [NSBundle bundleForClass:[self class]];
	return [bundle pathForResource:@"OttoMateAction" ofType:@"applescript" inDirectory:@"AppleScript"];
}


- (NSString *)appleScriptSource
{
	NSString *jsSource = [self javaScriptSource];
	
	NSString *asSource = [NSString stringWithContentsOfFile:[self appleScriptFilePath]];
	NSString *delay = [OttoMateAction delay];
	asSource = [NSString stringWithFormat:asSource, jsSource, delay];
	return asSource;
}


- (NSString *)javaScriptSource;
{
	[NSException raise:@"AssertionFailure" format:@"Class %@ must implement -[OttoMateAction javaScriptSource]", [self class]];
	return nil;
}


- (NSString *)javaScriptFilePath;
{
	[NSException raise:@"AssertionFailure" format:@"Class %@ must implement -[OttoMateAction javaScriptFilePath]", [self class]];
	return nil;
}


- (BOOL)isIdSelected
{
	return (0 == [[self objectForParameterKey:@"elementIdentifierType"] intValue]);
}


- (NSAppleEventDescriptor *)executeAndReturnError:(NSDictionary **)errorInfo
{
	NSAppleScript *script = [[NSAppleScript alloc] initWithSource:[self appleScriptSource]];
		
	NSAppleEventDescriptor *desc = [script executeAndReturnError:errorInfo];
	
	NSString *retVal = [desc stringValue];
	if ([retVal length] && [retVal hasPrefix:@"_TOD_ERROR: "]) {
		NSString *errStr = [retVal substringFromIndex:12];
		
		NSMutableDictionary *d = nil;
		if (*errorInfo) {
			d = [NSMutableDictionary dictionaryWithDictionary:(*errorInfo)];
		} else {
			d = [NSMutableDictionary dictionary];
		}
		
		[d setObject:[NSNumber numberWithInt:47] forKey:OSAScriptErrorNumber];
		[d setObject:errStr forKey:OSAScriptErrorMessage];
		[d setObject:errStr forKey:@"OttoMateError"];
		(*errorInfo) = d;
		
		NSLog([(*errorInfo) description]);

		[self runErrorHandlerScript:(*errorInfo)];
	}
	
	[script release];
	
	return desc;
}


- (void)runErrorHandlerScript:(NSDictionary *)errorInfo
{
	NSString *path = [OttoMateAction errorHandlerScriptPath];
	NSString *errStr = [errorInfo objectForKey:@"OttoMateError"];
	NSString *arg = [NSString stringWithFormat:@"'%@'", errStr];
	[self runScriptAtPath:path withArg:arg];
}


- (void)runAssertionFailedScript:(NSDictionary *)errorInfo
{
	NSString *path = [OttoMateAction assertionFailedScriptPath];
	NSString *errStr = [errorInfo objectForKey:@"OttoMateAssertionFailure"];
	NSString *errURL = [errorInfo objectForKey:@"OttoMateURL"];
	NSString *arg = [NSString stringWithFormat:@"%@ '%@'", errURL, errStr];
	[self runScriptAtPath:path withArg:arg];
}


- (void)runScriptAtPath:(NSString *)path withArg:(NSString *)arg
{
	if (![path length]) {
		return;
	}
	NSArray *args = [NSArray arrayWithObject:arg];
	@try {
		[NSTask launchedTaskWithLaunchPath:path arguments:args];
	}
	@catch (NSException *e) {
		NSLog(@"Error while attempting to run OttoMate script %@: %@", path, [e reason]);
	}
}


- (id)atomicInput
{
	id res = nil;
	if ([_input isKindOfClass:[NSArray class]] && [_input count]) {
		res = [_input objectAtIndex:0];
	} else {
		res = _input;
	}
	return res;
}


- (id)objectForParameterKey:(id)key;
{
	id result = [[self inputParameters] objectForKey:key];
	if (!result) {
		result = [[self parameters] objectForKey:key];
	}
	return result;
}


- (id)input
{
	return _input;
}


- (void)setInput:(id)newInput
{
	if (_input != newInput) {
		[_input autorelease];
		_input = [newInput retain];
	}
}


- (NSMutableDictionary *)inputParameters
{
	return _inputParameters;
}


- (void)setInputParameters:(NSMutableDictionary *)newDict
{
	if (_inputParameters != newDict) {
		[_inputParameters autorelease];
		_inputParameters = [newDict retain];
	}
}


#pragma mark -
#pragma mark Private

- (void)checkParametersForInputVariable
{
	NSEnumerator *e = [[self parameters] keyEnumerator];
	NSString *key = nil;
	id val = nil;
	while (key = [e nextObject]) {
		val = [[self parameters] objectForKey:key];
		if ([val isKindOfClass:[NSString class]] && [val isEqualToString:@"${input}"]) {
			[[self inputParameters] setObject:[[self atomicInput] stringValue] forKey:key];
		} else {
			[[self inputParameters] removeObjectForKey:key];
		}
	}
}


@end
